var vscode = require('vscode')
var R = require('ramda')
var { log } = require('./lib/LogEx')
var { 包含中文, 查找字段, 是纯字母 } = require('./lib/StringEx')
var { 数组去重 } = require('./lib/ArrayEx')
var 获得中文字符表示 = require('./lib/GetChineseExpression')
var 谷歌输入法API = require('./lib/IM_google')
var 百度搜索联想 = require('./lib/IM_baidu')
var 常用汉字 = require('./lib/CommonChinese')

function 获得当前输入字段() {
    // 我们需要的是文本被编辑后而非编辑前的情况。
    // 为避免意外获得文本被编辑前的情况，加个定时器，确保函数在文本修改后执行。
    // 这样函数就变成了异步的，于是加了Promise。
    return new Promise((res, rej) => {
        setTimeout(() => {
            var 编辑器 = vscode.window.activeTextEditor
            if (!编辑器) return res(null)

            var 光标位置 = 编辑器.selections[0].anchor;
            var 文件 = 编辑器.document
            var 范围 = 文件.getWordRangeAtPosition(光标位置)

            if (范围 == null) return res(null)

            var 当前输入字段 = 文件.getText(范围)
            return res(当前输入字段)
        }, 0)
    })
}

// vscode.executeCompletionItemProvider 会调用 provideCompletionItems 导致无限递归
// 因为 js 是单线程模型，所以只做了一个简单的锁，应该不会有问题
var 防递归锁 = false
async function provideCompletionItems(document, position, token, context) {
    var 用户配置 = {}
    用户配置.使用输入法 = vscode.workspace.getConfiguration('ChineseInputAssistant').get("InputMethod")
    用户配置.markdown补全 = vscode.workspace.getConfiguration('ChineseInputAssistant').get("markdownUse")
    用户配置.提示方式 = vscode.workspace.getConfiguration('中文代码快速补全').get("提示方式")
    用户配置.是否分析vscode提供的补全项 = vscode.workspace.getConfiguration('ChineseInputAssistant').get("useCompletionItem")
    用户配置.是否分析当前文档的词作为补全项 = vscode.workspace.getConfiguration('ChineseInputAssistant').get("useFileWord")
    // log('用户配置', 用户配置)

    if (document.languageId == 'markdown' && !(用户配置.markdown补全 == 'yes')) return []

    var 输入字段 = await 获得当前输入字段() || ''

    if (防递归锁) return []
    防递归锁 = true

    log('================')
    log('输入字段', 输入字段)

    // var 文件后缀名 = R.last(document.fileName.split('.'))
    // log('当前文件后缀名', 文件后缀名)

    var 补全项 = []

    if (用户配置.是否分析vscode提供的补全项 == 'yes') {
        var 补全项提供器 = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', document.uri, position)
        补全项提供器 = 补全项提供器.items
        补全项 = 补全项.concat(补全项提供器)
        // log('补全项提供器', 补全项提供器.map(a => a.label))
    }

    防递归锁 = false

    // 这个好像很卡
    // var 文档字段提供程序 = await vscode.commands.executeCommand('vscode.executeDocumentSymbolProvider', document.uri)
    // 文档字段提供程序 = 文档字段提供程序.map(a => new vscode.CompletionItem(a.name, a.kind))
    // 补全项 = 补全项.concat(文档字段提供程序)
    // log('文档字段提供程序', 文档字段提供程序.map(a => a.label))

    if (用户配置.是否分析当前文档的词作为补全项 == 'yes') {
        var 文档字段 = R.compose(R.filter(包含中文), 查找字段)(document.getText())
        文档字段 = 文档字段.map(a => new vscode.CompletionItem(a, vscode.CompletionItemKind.Text))
        补全项 = 补全项.concat(文档字段)
        // log('文档字段', 文档字段.map(a => a.label))
    }

    if (用户配置.使用输入法 != 'no') {
        // 如果分段输入一个词组，例如“多重笛卡尔积”，输入顺序是：先输入“多重”，再输入“笛卡尔积”。
        // 当输入到“多重d”的时候，变量`输入字段`值为“多重d”。如果直接拿这个词去调用输入法，很难得到正确的结果。
        // 但如果使用"d"去调用输入法，得到的结果并不以"多重"开头，导致不会弹出提示框。
        // 因此，这里的方案是，拆分非英文部分和英文部分，用英文部分调用输入法，再在结果前面补上非英文部分。
        // 注意：这个策略在一些情况下会失效，例如在中文当中输入时。
        var 非英文部分 = 输入字段.split('').filter(a => !是纯字母(a)).join('')
        var 英文部分 = 输入字段.split('').filter(a => 是纯字母(a)).join('')
        var 输入法提供的词 = []
        var 输入法 = null
        if (英文部分 != '' && 英文部分 != null) {
            if (用户配置.使用输入法 == 'Google Pinyin') {
                输入法 = 谷歌输入法API
            } else if (用户配置.使用输入法 == 'Baidu Sugrec') {
                输入法 = 百度搜索联想
            }
        }
        if (输入法 != null) {
            输入法提供的词 = await 输入法(英文部分).catch(e => {
                console.error(e)
                vscode.window.showInformationMessage(`调用 ${用户配置.使用输入法} 输入法接口出错：` + e)
            })
            输入法提供的词 = 输入法提供的词.map(a => 非英文部分 + a).map(a => new vscode.CompletionItem(a, vscode.CompletionItemKind.Text))
            补全项 = 补全项.concat(输入法提供的词)
        }
    }

    补全项 = 数组去重((a, b) => a.label == b.label, 补全项)
    // 过滤下面的部分
    // 1 不包含中文的关键词没必要加拼音,因此过滤
    // 2 现在正在输入的字段不需要加入补全项
    // 3 无论这个函数是否返回结果,vsc总会检测和自动补全定义的片段(Snippet),所以这里把片段过滤掉.
    补全项 = 补全项.filter(a => 包含中文(a.label))
        .filter(a => a.label != 输入字段)
        .filter(a => a.kind != vscode.CompletionItemKind.Snippet)
    补全项 = 补全项.map(a => R.set(R.lensProp('insertText'), a.label, a))

    补全项 = 补全项.map(a => R.set(R.lensProp('label'), `${a.label}\t${获得中文字符表示({
        表示方法: 用户配置.提示方式,
        文本: a.label,
        选项: {}
    })}`, a))

    log('补全项', 补全项.map(a => a.label))
    return new vscode.CompletionList(补全项, true)
}

function resolveCompletionItem(item, token) {
    return null
}

module.exports = function (context) {
    context.subscriptions.push(
        vscode.languages.registerCompletionItemProvider({
            scheme: 'file',
            language: '*'
        }, {
            provideCompletionItems,
            resolveCompletionItem
        }, '$', '.', ':', ...常用汉字)
    )
}
